﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MediaScraper.Common;
using MediaScraper.DataAccess;
using MediaScraper.DataAccess.Entities;

namespace MediaScraper.WindowsService
{
    /// http://www.codeproject.com/Articles/26528/C-Application-to-Watch-a-File-or-Directory-using-F
    public class Scraper
    {
        private readonly List<string> _extensions;
        private readonly FileSystemWatcher _objWatcher;

        public Scraper()
        {
            _extensions = Constants.DefaultVideoExtensions;
            _objWatcher = new FileSystemWatcher {Filter = "*.*", IncludeSubdirectories = true};

            _objWatcher.Created += OnCreated;

            using (var context = new DataContext())
            {
                var data = context.FileExtensions.ToList();
                foreach (var extension in data.Where(extension => _extensions.All(x => x != extension.ExtensionName)))
                {
                    _extensions.Add(extension.ExtensionName);
                }
            }
        }


        public void Start()
        {
            try
            {
                using (var context = new DataContext())
                {
                    var appConfig = context.ApplicationConfigurations.FirstOrDefault();
                    if (appConfig != null & appConfig.DownloadRootPathValid())
                    {
                        _objWatcher.Path = appConfig.DownloadRootPath;
                    }
                }
                _objWatcher.EnableRaisingEvents = true;
            }
            catch (Exception ex)
            {
            }
        }
    

    public void Stop()
        {
            _objWatcher.EnableRaisingEvents = false;
        }

        public void ProcessFile(string fullPath)
        {
            var agents = ObjectBroker.GetAllOfType<IMediaAgent>().ToList();
            if (!agents.Any()) return;

            var videoInfo = GetVideoInfoForDownloadedFile(agents, fullPath, GetSubtitleCultures());
            if (videoInfo == null) return;
            var adapter = ObjectBroker.Get<IMediaServerAdapter>();
            videoInfo = ProcessByAdapter(videoInfo, adapter, fullPath);
            if (!MoveFile(fullPath, videoInfo, adapter)) return;
            DownloadSubtitles(videoInfo, agents, adapter);
            DeleteSpamFiles(Path.GetDirectoryName(fullPath));
        }

        private VideoInfo ProcessByAdapter(VideoInfo videoInfo, IMediaServerAdapter adapter, string fullPath)
        {
            adapter.ProcessVideoInfo(videoInfo);
            videoInfo.FileProcessInfo.FileName += Path.GetExtension(fullPath);
            return videoInfo;
        }

        private static bool MoveFile(string fullPath, VideoInfo videoInfo, IMediaServerAdapter adapter)
        {
            var success = false;
            var destinationFolders = GetDestinationFolders(videoInfo, adapter);
             string destinationRoot = string.Empty;
            if (destinationFolders != null && destinationFolders.Any())
            {

                foreach (var destinationFolder in destinationFolders)
                {
                    var targetPath = Path.Combine(destinationFolder, videoInfo.FileProcessInfo.ParentFolder,
                                                  videoInfo.FileProcessInfo.FolderName);
                    if (Directory.Exists(targetPath))
                    {
                        destinationRoot = destinationFolder;
                        //File is series. Series/season path exists
                    }

                    targetPath = Path.Combine(destinationFolder, videoInfo.FileProcessInfo.ParentFolder);
                    if (Directory.Exists(targetPath))
                    {
                        destinationRoot = destinationFolder;
                        //File is series. Series path exists, season not
                        //or: file is movie and already existing
                    }
                }

                if (string.IsNullOrWhiteSpace(destinationRoot))
                {
                    destinationRoot = destinationFolders.FirstOrDefault();
                }
                var target = Path.Combine(destinationRoot, videoInfo.FileProcessInfo.ParentFolder,
                                          videoInfo.FileProcessInfo.FolderName);
                var destFileName = Path.Combine(target, videoInfo.FileProcessInfo.FileName);
                if (Directory.Exists(target))
                {
                    if (File.Exists(destFileName))
                    {
                        LogWriter.LogWarning(LoggingCategories.Scraper, string.Format("{0} already exists for Videofile {1}", target, videoInfo.Name));
                        //ToDo: Get from config if file should be overwritten
                        return false;
                    }
                    if (videoInfo is SeriesInfo)
                    {
                        MoveFile(fullPath, videoInfo, destFileName);
                        success = true;
                    }
                }
                else if (!Directory.Exists(target))
                {
                    Directory.CreateDirectory(target);
                    MoveFile(fullPath, videoInfo, destFileName);
                    success = true;
                }
                
            }
            return success;
        }

        private static void MoveFile(string fullPath, VideoInfo videoInfo, string destFileName)
        {
            File.Move(fullPath, destFileName);
            videoInfo.FileProcessInfo.DestinationFilePath = destFileName;
        }

    

        private static IList<string> GetDestinationFolders(VideoInfo videoInfo, IMediaServerAdapter adapter)
        {
            return videoInfo is SeriesInfo
                       ? adapter.GetSeriesRootFolders()
                       : adapter.GetMoviesRootFolder();
        }

        private void DownloadSubtitles(VideoInfo videoInfo, IEnumerable<IMediaAgent> agents, IMediaServerAdapter adapter)
        {
            foreach (var mediaAgent in agents)
            {
                foreach (var subTitleInfo in videoInfo.SubTitleInfos)
                {
                    var sub = mediaAgent.DownloadSubtitle(subTitleInfo.DownloadLink,
                                                          Path.GetDirectoryName(
                                                              videoInfo.FileProcessInfo.DestinationFilePath),
                                                          subTitleInfo.RawFileName);
                    
                    if (!string.IsNullOrWhiteSpace(sub) && File.Exists(sub))
                    {
                        var destFileName = adapter.GetSubtitleFileName(videoInfo, subTitleInfo.Culture);
                        destFileName += Path.GetExtension(subTitleInfo.RawFileName);
                        if (!File.Exists(Path.Combine(Path.GetDirectoryName(sub), destFileName)))
                        {
                            File.Move(sub, Path.Combine(Path.GetDirectoryName(sub), destFileName));        
                        }
                    }
                }
            }
        }

        private VideoInfo GetVideoInfoForDownloadedFile(IEnumerable<IMediaAgent> agents, string fullPath, List<CultureInfo> cultures)
        {
            return agents.Select(mediaAgent => mediaAgent.GetInfoByFile(fullPath, cultures) ?? mediaAgent.GetInfoByFileName(Path.GetFileName(fullPath), cultures)).FirstOrDefault(info => info != null);
        }

        private List<CultureInfo> GetSubtitleCultures()
        {
            try
            {
                var info = new List<CultureInfo>();
                using (var context = new DataContext())
                {
                    var appConfig = context.ApplicationConfigurations.FirstOrDefault();
                    if (appConfig != null)
                    {
                        info.AddRange(
                            appConfig.SubtitleCultures.Select(subtitleCulture => new CultureInfo(subtitleCulture,false)));
                    }
                }
                if (!info.Any())
                {
                    info = new List<CultureInfo>()
                        {
                            new CultureInfo("en-US", false)
                        };
                }
                return info;

            }
            catch (Exception ex) { }

            return new List<CultureInfo>()
                        {
                            new CultureInfo("en-US", false)
                        }; 
        }

        private ApplicationConfiguration GetApplicationConfiguration()
        {
            var applicationConfiguration = new ApplicationConfiguration();

            return applicationConfiguration;
        }

        private void OnCreated(object source, FileSystemEventArgs e)
        {
            var ext = (Path.GetExtension(e.FullPath) ?? string.Empty).ToLower();
            
            while (FileIsInUse(e.FullPath))
            {
                
            }
            try
            {
                if (_extensions.Any(ext.Equals) && !e.FullPath.ToLower().Contains("sample"))
                {
                    ProcessFile(e.FullPath);
                }
            }
            catch (Exception ex)
            {
             LogWriter.LogError(LoggingCategories.Scraper, "Error processing file.",ex);
            }
        }
        
        private bool FileIsInUse(string filePath)
        {
            FileStream stream = null;
            try
            {
                FileInfo file = new FileInfo(filePath);
                stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            }
            catch (IOException)
            {

                return true;
            }
            finally
            {
                if (stream != null)
                    stream.Close();
            }
            //file is not locked
            return false;
        }

        private void DeleteSpamFiles(string root)
        {
            try
            {
                var extractionFolder = new DirectoryInfo(root);

                foreach (FileInfo file in extractionFolder.GetFiles())
                {
                    var ext = (Path.GetExtension(file.FullName) ?? string.Empty).ToLower();

                    if (!_extensions.Any(ext.Equals) || file.FullName.ToLower().Contains("sample"))
                    {
                        file.Delete();
                    }
                }
                //Delete subfolders
                foreach (DirectoryInfo dir in extractionFolder.GetDirectories())
                {
                    dir.Delete(true);
                }

                var parent = Directory.GetParent(root);
                if (_objWatcher != null && parent.FullName != _objWatcher.Path)
                {
                    parent.Delete(true);
                }
            }
            catch (Exception ex)
            {
            }
        }
    }
}
